/*
 *  Copyright (c) 2012 The mraid-web-tester project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */


 /*
  * Code flow is
  * (0) prepareMraidView initializes version and "supports" properties
INFO MRAID version 2.0
INFO placement type inline
INFO [sms,tel,calendar,storePicture,inlineVideo]
  * (1) buttons on interface call renderAd()  - Flight tab>ad fragment or renderHtmlAd - Flight tab>ad url
INFO rendering
INFO creating adWindow
  *   (.) onload event of new browser window
INFO adWindow loaded
  * (2) render() calls initAdFrame()
INFO initializing ad frame
  * (3) initAdFrame() calls initAdBridge() and loadHtml()
INFO initializing bridge object [object Object]
  * (4) initAdBridge() calls
  *   (a) EventListeners.add for info reporting
INFO activating info
  *   (b) EventListeners.add for error reporting
INFO activating error
  *   (c) pushChange() for initialization of all other properties
INFO controller initialized
INFO setting state to loading
INFO setting screenSize to {'width':320,'height':480}
INFO setting orientation to 0
INFO setting size to {'width':320,'height':50}
INFO setting default position to {'width':320,'height':50,'y':0,'x':0}
INFO setting maxSize to {'width':320,'height':480}
INFO merging expandProperties with {'width':0,'height':0,'useCustomClose':false,'isModal':false}
INFO setting supports to [screen]

  * (5) pushChange() calls the addEventListener() method in mraid-main.js
  * (6) addEventListener() calls changeHandlers.[listener]
  * (7) changeHandlers.state() through signalReady, send ready event
INFO activating ready
  * (8) identification script loaded
INFO mraid.js identification script found
  */

(function() {
    var mraidview = window.mraidview = {};

    // CONSTANTS ///////////////////////////////////////////////////////////////

    var VERSIONS = mraidview.VERSIONS = {
        V1  : '1.0',
        V2  : '2.0'
    };

    var PLACEMENTS = mraidview.PLACEMENTS = {
        UNKNOWN      : 'unknown',

        INLINE       : 'inline',
        INTERSTITIAL : 'interstitial'
    }

    var STATES = mraidview.STATES = {
        UNKNOWN     :'unknown',

        LOADING     :'loading',
        DEFAULT     :'default',
        RESIZED     :'resized',
        EXPANDED    :'expanded',
        HIDDEN      :'hidden'
    };

    var EVENTS = mraidview.EVENTS = {
        INFO                :'info',
        ORIENTATIONCHANGE   :'orientationChange',

        READY               :'ready',
        ERROR               :'error',
        STATECHANGE         :'stateChange',
        VIEWABLECHANGE      :'viewableChange',
        SIZECHANGE          :'sizeChange',
    };

    var FEATURES = mraidview.FEATURES = {
        SMS         :'sms',
        TEL         :'tel',
        CALENDAR    :'calendar',
        STOREPICTURE:'storePicture',
        INLINEVIDEO :'inlineVideo'
    };

    // EVENT HANDLING ///////////////////////////////////////////////////////////////

    var listeners = {};

    var broadcastEvent = function() {
        var args = new Array(arguments.length);
        for (var i = 0; i < arguments.length; i++) args[i] = arguments[i];

        var event = args.shift();

        for (var key in listeners[event]) {
            var handler = listeners[event][key];
            handler.func.apply(handler.func.scope, args);
        }
    }

    mraidview.broadcastEvent = broadcastEvent;

    mraidview.addEventListener = function(event, listener, scope) {
        var key = String(listener) + String(scope);
        var map = listeners[event]
        if (!map) {
            map = {};
            listeners[event] = map;
        }
        map[key] = {scope:(scope?scope:{}),func:listener};
    };

    mraidview.removeEventListener = function(event, listener, scope) {
        var key = String(listener) + String(scope);
        var map = listeners[event];
        if (map) {
            map[key] = null;
            delete map[key];
        }
    };

    // PRIVATE VARIABLES ///////////////////////////////////////////////////////////////

    var
        adURI = "",
        adURIFragment = true,
        adHtml = '',
        useHtml = false;
        adContent = '',
        adWindow = null,
        adWindowAdj = {x:0,y:0},
        adFrame = null,
        adFrameExpanded = null,
        adContainer = null,
        adResizeContainer = null,
        adExpandedContainer = null,
        closeEventRegion = null,
        adBridge = null,
        adController = null,
        inactiveAdBridge = null,
        inactiveAdController = null,
        intervalID = null,
        timeoutID = null,
        active = {},
        previousPosition = { x:0, y:0, width:0, height:0 },
        previousState = null,
        defaultWindowSize = null,
        adContainerOrientation = -1;

    // MRAID state variables - shared with frame
    var
        state = STATES.LOADING,
        screenSize = { width:0, height:0 },
        size = { width:0, height:0 },
        defaultPosition = { width:0, height:0, y:0, x:0 },
        c = { width:0, height:0, y:0, x:0 },
        maxSize = { width:0, height:0, x:0, y:0 },
        expandProperties = { width:0, height:0, useCustomClose:false, isModal:false},
        orienationProperties = { allowOrientationChange:true, forceOrientation:'none' },
        resizeProperties = { initialized: false, validated: false, width:0, height:0, customClosePosition:'top-right', offsetX:undefined, offsetY:undefined, allowOffscreen:true},
        supports = [],
        version = VERSIONS.UNKNOWN,
        placement = PLACEMENTS.UNKNOWN,
        isViewable = false;
        orientation = -1;

    // PUBLIC ACCESSOR METHODS ///////////////////////////////////////////////////////////////

    mraidview.getAdContent = function() {
        return adContent;
    };

    mraidview.setScreenSize = function(width, height) {
        screenSize.width = width;
        screenSize.height = height;
        orientation = (width >= height)?90:0;
        adContainerOrientation = orientation;
    };

    mraidview.setDefaultPosition = function(x, y, width, height) {
        defaultPosition.x = parseInt(x);
        defaultPosition.y = parseInt(y);
        defaultPosition.width = parseInt(width);
        defaultPosition.height = parseInt(height);
        currentPosition = defaultPosition;
    };

    mraidview.setMaxAdPosition= function(x, y, width, height) {
        maxSize.x = x;
        maxSize.y = y;
        if (orientation % 180 === 0) {
            maxSize.width = width;
            maxSize.height = height;
        } else {
            maxSize.width = height;
            maxSize.height = width;
        }
    };

    mraidview.setAdURI = function(uri, fragment) {
        adURI = uri;
        adURIFragment = (fragment)?true:false;
    };

    mraidview.setUseHtml = function(useThisHtml, html) {
        useHtml = useThisHtml;
        if (useHtml) {
            adHtml = html;
        } else {
            adHtml = '';
        }
    };

    mraidview.resetSupports = function() {
        supports = [];
    };

    mraidview.setSupports = function(feature, doesSupport) {
        if (doesSupport) {
            supports.push(feature);
            broadcastEvent(EVENTS.INFO, stringify(supports));
        }
    };

    mraidview.setVersion = function(elements) {
        var v = '';
        for (var i=0; i<elements.length; i++) {
            if (elements[i].checked) v = elements[i].value;
        }
        mraidview.version = v;
        broadcastEvent(EVENTS.INFO, 'MRAID version ' + v);
    };

    mraidview.setPlacement = function(elements) {
        var p = '';
        for (var i=0; i<elements.length; i++) {
            if (elements[i].checked) p = elements[i].value;
        }
        mraidview.placement = p;
        placement = p;
        broadcastEvent(EVENTS.INFO, 'placement type ' + p);
    };

    mraidview.setOffScreen  = function(value) {
        offscreen = value;
    };

    mraidview.rotateOrientation = function() {
        mraidview.setOrientation(orientation = (orientation + 90) % 180);
    };

    mraidview.setOrientation = function (degree, forceOrientation) {
        if (degree%90 !== 0) return;
        if (!adWindow || !adWindow.document) return;
        var body = adWindow.document.getElementsByTagName('body')[0],
            maxDiv = adWindow.document.getElementById('maxArea');

        maxDiv.style['-webkit-transform'] = '';

        if (degree%180 === 0) { // Portrait
            adWindow.resizeTo(defaultWindowSize.outerWidth, defaultWindowSize.outerHeight);

            body.style.width = Math.min(screenSize.width, screenSize.height) + 'px';
            body.style.height = Math.max(screenSize.width, screenSize.height) + 'px';

            if (orientationProperties.forceOrientation === 'landscape' && (!orientationProperties.allowOrientationChange || forceOrientation)
                && (state === STATES.EXPANDED || placement === PLACEMENTS.INTERSTITIAL)) {

                maxDiv.style['-webkit-transform'] = 'rotate(90deg)';
                var dx = (maxSize.height - maxSize.width)/2;
                updateAdSize({'width':maxSize.height,'height':maxSize.width,'x':maxSize.y-dx, 'y':maxSize.x+dx});
                if (state === STATES.EXPANDED) {
                    setAdResizeContainerStyle((adExpandedContainer || adResizeContainer), {'width':maxSize.height,'height':maxSize.width,'x':0, 'y':0});
                } else if (placement === PLACEMENTS.INTERSTITIAL) {
                    adContainer.style.width = defaultPosition.height + 'px';
                    adContainer.style.height = defaultPosition.width + 'px';
                    adContainer.style.top = defaultPosition.y + 'px';
                    adContainer.style.left = defaultPosition.x + 'px';
                }
                setAdOrientation(90);
            } else {
                updateAdSize(maxSize);
                if (state === STATES.EXPANDED) {
                    setAdResizeContainerStyle((adExpandedContainer || adResizeContainer), {'width':maxSize.width,'height':maxSize.height,'x':0, 'y':0});
                } else if (placement === PLACEMENTS.INTERSTITIAL) {
                    setContainerDefaultPosition();
                }
                setAdOrientation(0);
            }
        } else { // Landscape
            var w = (defaultWindowSize.outerWidth - defaultWindowSize.innerWidth) + defaultWindowSize.innerHeight,
                h = (defaultWindowSize.outerHeight - defaultWindowSize.innerHeight) + defaultWindowSize.innerWidth;
            adWindow.resizeTo(w, h);

            body.style.height = Math.min(screenSize.width, screenSize.height) + 'px';
            body.style.width = Math.max(screenSize.width, screenSize.height) + 'px';

            if (orientationProperties.forceOrientation === 'portrait' && (!orientationProperties.allowOrientationChange || forceOrientation)
                && (state === STATES.EXPANDED || placement === PLACEMENTS.INTERSTITIAL)) {

                maxDiv.style['-webkit-transform'] = 'rotate(90deg)';
                var dx = (maxSize.height - maxSize.width)/2;
                updateAdSize({'width':maxSize.width,'height':maxSize.height,'x':maxSize.y+dx, 'y':maxSize.x-dx});
                if (state === STATES.EXPANDED) {
                    setAdResizeContainerStyle((adExpandedContainer || adResizeContainer), {'width':maxSize.width,'height':maxSize.height,'x':0, 'y':0});
                } else if (placement === PLACEMENTS.INTERSTITIAL) {
                    setContainerDefaultPosition();
                }
                setAdOrientation(0);
            } else {
                updateAdSize({'width':maxSize.height,'height':maxSize.width,'x':maxSize.x, 'y':maxSize.y})
                if (state === STATES.EXPANDED) {
                    setAdResizeContainerStyle((adExpandedContainer || adResizeContainer), {'width':maxSize.height,'height':maxSize.width,'x':0, 'y':0});
                } else if (placement === PLACEMENTS.INTERSTITIAL) {
                    adContainer.style.width = defaultPosition.height + 'px';
                    adContainer.style.height = defaultPosition.width + 'px';
                    adContainer.style.top = defaultPosition.y + 'px';
                    adContainer.style.left = defaultPosition.x + 'px';
                }
                setAdOrientation(90);
            }
        }
        if (offscreen) {
            adWindow.setNavigation();
        }
    };

    mraidview.setDefaultWindowSize = function () {
        defaultWindowSize = {};
        if (orientation % 180 === 0) {
            defaultWindowSize.outerWidth = adWindow.outerWidth;
            defaultWindowSize.innerWidth = adWindow.innerWidth;
            defaultWindowSize.outerHeight = adWindow.outerHeight;
            defaultWindowSize.innerHeight = adWindow.innerHeight;
        } else {
            defaultWindowSize.innerWidth = adWindow.innerHeight;
            defaultWindowSize.innerHeight = adWindow.innerWidth;
            defaultWindowSize.outerWidth = defaultWindowSize.innerWidth + (adWindow.outerWidth - adWindow.innerWidth); // + (adWindow.outerHeight - adWindow.innerHeight);
            defaultWindowSize.outerHeight = defaultWindowSize.innerHeight + (adWindow.outerHeight - adWindow.innerHeight);
        }
    };

    // PUBLIC ACTION METHODS ///////////////////////////////////////////////////////////////

    mraidview.render = function() {
        broadcastEvent(EVENTS.INFO, 'rendering');

        if (!adFrame || !adWindow || !adWindow.document || !adFrame.contentWindow) {
            broadcastEvent(EVENTS.INFO, 'creating adWindow');
            adWindow = window.open((offscreen) ? 'safari/device-pages.html': 'safari/device.html', 'adWindow', 'left=1000,width='+screenSize.width+',height='+screenSize.height+',menubar=no,location=no,toolbar=no,status=no,personalbar=no,resizable=no,scrollbars=no,chrome=no,all=no');

            adWindow.onload = function() {
                broadcastEvent(EVENTS.INFO, 'adWindow loaded');

                adWindowAdj.x = window.outerWidth - screenSize.width;
                adWindowAdj.y = window.outerHeight - screenSize.height;
                adWindow.document.getElementsByTagName('body')[0].style.width = screenSize.width + 'px';
                adWindow.document.getElementsByTagName('body')[0].style.height = screenSize.height + 'px';
                adContainer = adWindow.document.getElementById('adContainer');
                adContainer.addEventListener('ViewableChange', function(e) {
                    changeViewable();
                });
                adResizeContainer = adWindow.document.getElementById('adResizeContainer');
                adFrame = adWindow.document.getElementById('adFrame');
                adFrame.contentWindow.orientation = 0; // Set initial orientation.
                closeEventRegion = adWindow.document.getElementById('closeEventRegion');
                closeEventRegion.addEventListener('click', closeAd);

                window.setTimeout(function () { //timeout needed to get the actual window size
                    mraidview.setDefaultWindowSize();
                    if (offscreen) {
                        adWindow.setNavigation();
                    }
                }, 250);
                loadAd();
            };
        } else {
            adWindow.close();
            adWindow = null;
             mraidview.render();
        }
    };

    // PRIVATE METHODS ///////////////////////////////////////////////////////////////

    var clone = function(obj) {
        var f = function() {};
        f.prototype = obj;
        return new f();
    };

    var stringify = function(obj) {
        if (typeof obj == 'object') {
            if (obj.push) {
                var out = [];
                for (var p = 0; p < obj.length; p++) {
                    out.push(obj[p]);
                }
                return '[' + out.join(',') + ']';
            } else {
                var out = [];
                for (var p in obj) {
                    out.push('\''+p+'\':'+obj[p]);
                }
                return '{' + out.join(',') + '}';
            }
        } else {
            return String(obj);
        }
    };

    var reset = function() {
        adContent = '';
        adBridge = null;
        adController = null;
        adFrame.style.display = 'block';
        adContainer.style.display = 'block';
        adResizeContainer.style.display = 'block';
        intervalID = null;
        timeoutID = null;
        active = {};
        size.width = defaultPosition.width;
        size.height = defaultPosition.height;
        previousPosition = { x:0, y:0, width:0, height:0 };
        previousState = null;
        state = STATES.DEFAULT;
        expandProperties = { width:maxSize.width, height:maxSize.height, useCustomClose:false, isModal:false};
        resizeProperties = { initialized: false, validated: false, width:0, height:0, customClosePosition:'top-right', offsetX:undefined, offsetY:undefined, allowOffscreen:true};
        orientationProperties = {allowOrientationChange:true, forceOrientation:'none'};
        orientation = (screenSize.width >= screenSize.height)?90:0;
        version = VERSIONS.UNKNOWN;
        currentPosition = { 'x':0, 'y':0, 'width':defaultPosition.width, 'height':defaultPosition.height };
        isViewable = false;
    };

    var showMraidCloseButton = function(toggle) {
        var closeDiv = closeEventRegion;
        closeDiv.style.position = 'absolute';

        if (toggle) {
            closeDiv.style.top = '';
            closeDiv.style.left = '';
            closeDiv.style.bottom = '';
            closeDiv.style.right = '';
            closeDiv.style.width = '50px';
            closeDiv.style.height = '50px';
            closeDiv.style.display = 'none';
            closeDiv.style.zIndex = getHighestZindex()+2;
            closeDiv.style.cursor = 'pointer';
            closeDiv.style.background = (expandProperties.useCustomClose) ? '': 'url("close.png") no-repeat 8px 8px';

            if (state === STATES.RESIZED) {
                closeDiv.style.background = '';

                var pos = resizeProperties.customClosePosition;
                if (/top/i.test(pos)) {
                    closeDiv.style.top = '0px';
                } else if (/bottom/i.test(pos)) {
                    closeDiv.style.bottom = '0px';
                } else {
                    closeDiv.style.top = [(resizeProperties.height - 50 ) / 2, 'px'].join('');
                }

                if (/left/i.test(pos)) {
                    closeDiv.style.left = '0px';
                } else if (/right/i.test(pos)) {
                    closeDiv.style.right = '0px';
                } else {
                    closeDiv.style.left = [(resizeProperties.width - 50 ) / 2, 'px'].join('');
                }
            } else {
                closeDiv.style.top = '0';
                closeDiv.style.right = '0';
            }

            closeDiv.style.display = 'block';
            broadcastEvent (EVENTS.INFO, 'adding MRAID close button');
        } else {
            closeDiv.style.display = 'none';
            broadcastEvent (EVENTS.INFO, 'removing MRAID close button');
        }
    };

    var loadAd = function() {
        reset();

        if (adFrame.attachEvent) {
            adFrame.attachEvent("onload", initAdFrame);
        } else {
            adFrame.onload = initAdFrame;
        }

        setContainerDefaultPosition(defaultPosition);

        if (useHtml) {
            var doc = adFrame.contentWindow.document;
            doc.body.innerHTML = '<body style="margin: 0px;"><div id="_mraidCloseDiv" onclick="mraid.close()"></div><div id="adHtml"></div></body>';
            doc.body.style.margin = '0px';
            initAdFrame.call(adFrame);
        } else {
            if (adURIFragment) {
                document.cookie = 'uri='+encodeURIComponent(adURI);
                adFrame.contentWindow.location.replace('ad.html');
            } else {
                adFrame.contentWindow.location.replace(adURI);
            }
        }

        if (orientation%180 === 0){
            setMaxAdArea(maxSize);
        } else {
            setMaxAdArea({'width':maxSize.height, 'height': maxSize.width, 'x': maxSize.x, 'y': maxSize.y});
        }
    };

    var loadHtml = function(adHtml) {
        var doc = adFrame.contentWindow.document,
            adDiv = doc.getElementById('adHtml'),
            scripts,
            scriptsCount,
            script,
            i;
        if (!adHtml || !adDiv) {
            return;
        }
        broadcastEvent(EVENTS.INFO, 'loading ad html');
        adDiv.innerHTML = adHtml;
        scripts = doc.body.getElementsByTagName("script");
        scriptsCount = scripts.length;
        for (i=0; i<scriptsCount; i++) {
            script = doc.createElement('script');
            script.type = "text/javascript";
            if (scripts[i].src !== '') {
                script.src = scripts[i].src;
            } else {
                script.text = scripts[i].text;
            }
            doc.body.appendChild(script);
        }
    };

    var insertAdURI = function(newAdFrame, uri) {
        var qs = 'htmlproxy.php?url=' + encodeURIComponent(uri),
            success,
            twoPartHtml = '';

        success = (function () {
            return function (data) {
                var scripts = '<script type="text/javascript" src="mraidview-bridge.js"></script><script type="text/javascript" src="mraid-main.js"></script>',
                    headStart,
                    headEnd,
                    headContent = ''; // Grab the contents of the head. We'll add these later so we have time to initialize mraid before any inline scripts run.
                twoPartHtml = data;
                headStart = data.indexOf('<head>') + 6;
                headEnd = data.indexOf('</head>');
                headContent = data.substr(headStart, headEnd - headStart);
                twoPartHtml = twoPartHtml.replace(headContent, scripts);
                newAdFrame.src = 'javascript: ' + twoPartHtml;

                if (newAdFrame.contentWindow.document.readyState === 'complete') {
                    init2PartAdFrame(headContent);
                } else {
                    if (newAdFrame.attachEvent) {
                        newAdFrame.attachEvent("onload", function () {
                            init2PartAdFrame(headContent);
                        });
                    } else {
                        newAdFrame.onload = function () {
                            init2PartAdFrame(headContent);
                        };
                    }
                }
                if (adURIFragment) {
                    document.cookie = 'uri='+encodeURIComponent(uri);
                }
            };
        })();
        
        try {
            if (window.jQuery !== undefined) {
                jQuery.get(qs, success);
            }
        } catch(e) {

        }
    };

    var setContainerDefaultPosition = function() {
        adContainer.style.left = defaultPosition.x + 'px';
        adContainer.style.top = defaultPosition.y + 'px';
        adContainer.style.width = defaultPosition.width + 'px';
        adContainer.style.height = defaultPosition.height + 'px';
    };

    var resizeAd = function() {
        adContainer.style.overflow = 'visible';
        var arcs = adResizeContainer.style;
        arcs.position = 'absolute';
        arcs.top = [resizeProperties.offsetY, 'px'].join('');
        arcs.left = [resizeProperties.offsetX, 'px'].join('');
        arcs.width = [resizeProperties.width, 'px'].join('');
        arcs.height = [resizeProperties.height, 'px'].join('');
        arcs['z-index'] = getHighestZindex()+1;
        currentPosition.x = defaultPosition.x + resizeProperties.offsetX;
        currentPosition.y = defaultPosition.y + resizeProperties.offsetY;
        currentPosition.width = resizeProperties.width;
        currentPosition.height = resizeProperties.height;
        size.width = currentPosition.width =  resizeProperties.width;
        size.height = currentPosition.height =  resizeProperties.height;
        adBridge.pushChange({ 'currentPosition': currentPosition, 'size': size});
    };

    var setMaxAdArea = function (size) {
        var maxDiv = adWindow.document.getElementById('maxArea');
        maxDiv.style.width = [size.width, 'px'].join('');
        maxDiv.style.height = [size.height, 'px'].join('');
        maxDiv.style.position = 'absolute';
        maxDiv.style.left = [size.x, 'px'].join('');
        maxDiv.style.top = [size.y, 'px'].join('');
        !adBridge || adBridge.pushChange({'maxSize': size});
    };

    var setExpandProperties = function(size){
        !adBridge || adBridge.pushChange({'expandProperties': size});
    };

    var setAdResizeContainerStyle = function (resizeContainer, _maxSize) {
        var acs = resizeContainer.style;
        if (mraidview.version == VERSIONS.V2) {
            if (adExpandedContainer) {
                acs.left = [0, 'px'].join('');
                acs.top = [0, 'px'].join('');
            } else {
                acs.left = ['-', defaultPosition.x, 'px'].join('');
                acs.top = ['-', defaultPosition.y, 'px'].join('');
            }

            acs.width = [_maxSize.width, 'px'].join('');
            acs.height = [_maxSize.height, 'px'].join('');
            currentPosition.width = size.width = _maxSize.width;
            currentPosition.height = size.height = _maxSize.height;
        } else if (mraidview.version === VERSIONS.V1) {
            var left = _maxSize.x + (_maxSize.width - expandProperties.width)/2,
                top = _maxSize.y + (_maxSize.height - expandProperties.height)/2;

            acs.left = [0, 'px'].join('');
            acs.top = [0, 'px'].join('');
            acs.width = [expandProperties.width, 'px'].join('');
            acs.height = [expandProperties.height, 'px'].join('');
            currentPosition.height = size.height = expandProperties.height;
            currentPosition.width = size.width = expandProperties.width;
        }
    };

    var resetDefaultSize = function () {
        adResizeContainer.style.overflow = 'hidden';
        var arcs = adResizeContainer.style;
        arcs.position = 'static';
        arcs.top = '0';
        arcs.left = '0';
        arcs.width = '100%';
        arcs.height = '100%';
        adContainer['z-index'] = 0;
        size.width = currentPosition.width =  defaultPosition.width;
        size.height = currentPosition.height =  defaultPosition.height;
        adBridge.pushChange({'size': size});
    };

    var getSupports = function(feature) {
        for (var i=0; i<supports.length; i++) {
            if (supports[i] == feature) return true;
        }
        return false;
    };

    var getHighestZindex = function() {
        var zi = 0,
            eles = document.getElementsByTagName('*');

        for (var i = 0, ele; ele = eles[i]; i++){
            if (ele.style['z-index'] && parseInt(ele.style['z-index']) > zi) {
                zi = parseInt(ele.style['z-index']);
            }
        }
        return zi;
    };

    var endExpanded = function() {
        if (adExpandedContainer) {
            adResizeContainer.appendChild(closeEventRegion);
            adExpandedContainer.parentNode.removeChild(adExpandedContainer);
            adExpandedContainer = null;
            adFrameExpanded = null;

        } else {
            resetDefaultSize();
        }
        mraidview.setOrientation(orientation);
    };

    var setResizeProperties = function(properties) {
        resizeProperties.validated = false;

        if (!properties || !properties.width || !properties.height) {
            adFrame.contentWindow.broadcastEvent(EVENTS.ERROR, 'missing properties for setResizeProperties' , 'setResizeProperties');
        }

        var regex = new RegExp('^(((top|bottom)-(left|right|center))|center)$', 'i');
        if (!regex.test(resizeProperties.customClosePosition)) {
            resizeProperties.customClosePosition = 'top-left';
        }

        if (!properties.allowOffscreen) {
            if (properties.width > maxSize.width || properties.height > maxSize.height) {
                adFrame.contentWindow.broadcastEvent(EVENTS.ERROR, 'invalid properties for setResizeProperties: width or height is too big' , 'setResizeProperties');
            } else {
                var posX = Math.max(0, Math.min(maxSize.width - properties.width, defaultPosition.x + properties.offsetX)),
                    posY = Math.max(0, Math.min(maxSize.height - properties.height, defaultPosition.y + properties.offsetY));

                properties.offsetX = posX - defaultPosition.x;
                properties.offsetY = posY - defaultPosition.y;
                properties.validated = true;
                setResizePropertyValues(properties);
            }
        } else {
            var pos = properties.customClosePosition,
                closeOffsetX = 0,
                closeOffsetY = 0,
                closeTotalPositionX = 0,
                closeTotalPositionY = 0;

            if (/top/i.test(pos)) {
                closeOffsetY = 0;
            } else if (/bottom/i.test(pos)) {
                closeOffsetY = properties.height-50;
            } else {
                closeOffsetY = properties.height/2-25;
            }

            if (/left/i.test(pos)) {
                closeOffsetX = 0;
            } else if (/right/i.test(pos)) {
                closeOffsetX = properties.width-50;
            } else {
                closeOffsetX = properties.width/2-25;
            }

            closeTotalPositionX = defaultPosition.x + properties.offsetX + closeOffsetX;
            closeTotalPositionY = defaultPosition.y + properties.offsetY + closeOffsetY;

            if (closeTotalPositionX < 0 ||
                closeTotalPositionX > maxSize.width - 50 ||
                closeTotalPositionY < 0 ||
                closeTotalPositionY > maxSize.height - 50) {

                //broadcastEvent(EVENTS.ERROR, 'invalid properties for setResizeProperties' , 'setResizeProperties');
                adFrame.contentWindow.broadcastEvent(EVENTS.ERROR, 'invalid properties for setResizeProperties', 'setResizeProperties');
            } else {
                properties.validated = true;
                setResizePropertyValues(properties);
            }
        }
        adBridge.pushChange({'resizeProperties':resizeProperties});
    };

    var setOrientationProperties = function (properties) {
        if (!properties) return;
        if (properties.forceOrientation) {
            orientationProperties.forceOrientation = properties.forceOrientation;
            if (defaultWindowSize === null) {
                mraidview.setDefaultWindowSize();
            }
            mraidview.setOrientation(orientation, true);
        }

        if (typeof(properties.allowOrientationChange) === 'boolean') {
            orientationProperties.allowOrientationChange = properties.allowOrientationChange;
        }
    };

    var setResizePropertyValues = function(properties) {
        for (var property in resizeProperties) {
            if (properties && typeof (properties[property]) !== 'undefined'  && properties[property] !== '')
                resizeProperties[property] = properties[property];
        }
        if (resizeProperties.initialized === false) {
            resizeProperties.initialized = true;
        }
    };

    var closeAd = function () {
        showMraidCloseButton(false);
        if (state === STATES.DEFAULT) {
            adBridge.broadcastEvent('hide');
            state = STATES.HIDDEN;
            adBridge.pushChange({'state':state});
        } else if (state === STATES.EXPANDED) {
            state = STATES.DEFAULT;
            if (inactiveAdBridge) {
                adBridge = inactiveAdBridge;
                adController = inactiveAdController;
                inactiveAdBridge = null;
                adController = null;
            }
            adBridge.pushChange({'state':state});
            endExpanded();
        } else if (state === STATES.RESIZED) {
            state = STATES.DEFAULT;
            adBridge.pushChange({'state':state});
            resetDefaultSize();
        } else {
            return;
        }

        adBridge.pushChange({'currentPosition':currentPosition});
        repaintAdWindow();
    };

    var repaintAdWindow = function () {
        if (!adWindow) return;
        adWindow.resizeBy(-1,0);
        adWindow.resizeBy(1,0);
    };

    var initAdBridge = function(bridge, controller) {
        broadcastEvent(EVENTS.INFO, 'initializing bridge object ' + bridge + controller);

        inactiveAdBridge = adBridge;
        inactiveAdController = adController;

        adBridge = bridge;
        adController = controller;

        if (placement === PLACEMENTS.INTERSTITIAL) {
            showMraidCloseButton(true);
        }

        bridge.addEventListener('activate', function(service) {
            active[service] = true;
        }, this);

        bridge.addEventListener('deactivate', function(service) {
            if (active[service]) {
                active[service] = false;
            }
        }, this);

        bridge.addEventListener('expand', function(uri) {
            if (state === STATES.HIDDEN || state === STATES.EXPANDED || state === STATES.UNKNOWN || state === STATES.LOADING) {
                return;
            }
            state = STATES.EXPANDED;
            showMraidCloseButton(true);

            var ac = adResizeContainer;
                acs = adResizeContainer.style,
                topAdContainer = adContainer;
            if (uri && uri !== '') {
                state = STATES.LOADING;

                adExpandedContainer = document.createElement('div');
                adFrameExpanded = document.createElement('iframe');
                adFrameExpanded.setAttribute('scrolling', 'no');
                adFrameExpanded.setAttribute('frameborder', '0');
                adFrameExpanded.setAttribute('id', 'adFrameExpanded');
                adFrameExpanded.style.height = '100%';
                adFrameExpanded.style.width = '100%';
                adFrameExpanded.style.position = 'absolute';
                adFrameExpanded.style.overflow = 'hidden';
                adFrameExpanded.style.padding = '0px';
                adFrameExpanded.style.margin = '0px';
                adFrameExpanded.style.border = 'none';
                adFrameExpanded.style['z-index'] = getHighestZindex()+1;
                ac = adExpandedContainer;

                insertAdURI(adFrameExpanded, uri);
                    
                adExpandedContainer.appendChild(adFrameExpanded);
                adExpandedContainer.appendChild(closeEventRegion);
                adContainer.parentNode.appendChild(adExpandedContainer);
                
                adExpandedContainer.style['z-index'] = getHighestZindex()+1;
                topAdContainer = adFrameExpanded;
            }

            acs.position = 'absolute';

            setAdResizeContainerStyle(ac, maxSize);

//            topAdContainer['z-index'] = getHighestZindex()+1;

            if (!uri || uri == '') {
                adBridge.pushChange({'state':state, 'currentPosition': currentPosition, 'size': size });
                broadcastEvent(EVENTS.INFO, 'expanding one-part ad');
                repaintAdWindow();

            } else {

                broadcastEvent(EVENTS.INFO, 'expanding two-part ad: ' + uri);
            }
            mraidview.setOrientation(orientation, true);

        }, this);

        bridge.addEventListener('close', closeAd , this);

        bridge.addEventListener('hide', function() {
            adFrame.style.display = 'none';
            adResizeContainer.disabled = 'none';
            adContainer.style.display = 'none';
            previousState = state;
            state = STATES.HIDDEN;
            adBridge.pushChange({ state:state, isViewable:false });
        }, this);

        bridge.addEventListener('show', function() {
            adFrame.style.display = 'block';
            adResizeContainer.style.display = 'block';
            adContainer.style.display = 'block';
            state = previousState;
            adBridge.pushChange({ state:state });
        }, this);

        bridge.addEventListener('open', function(URL) {
            broadcastEvent(EVENTS.INFO, 'opening ' + URL);
            window.open(URL, '_blank', 'left=1000,width='+screenSize.width+',height='+screenSize.height+',menubar=no,location=no,toolbar=no,status=no,personalbar=no,resizable=no,scrollbars=no,chrome=no,all=no');
        }, this);

        bridge.addEventListener('playVideo', function(URL) {
            broadcastEvent(EVENTS.INFO, 'playing ' + URL);
            window.open(URL, '_blank');
        }, this);

        bridge.addEventListener('storePicture', function(URL) {
            var allow = confirm('CONFIRM: Store this image to gallery?\nURL:' + URL);
            if (allow) {
                window.open('../imageDownload.php?imageUrl=' + URL);
                broadcastEvent(EVENTS.INFO, 'storing the image ' + URL);
            } else {
                adFrame.contentWindow.broadcastEvent(EVENTS.ERROR, 'Permission denied by user', 'storePicture');
            }
        }, this);

        bridge.addEventListener('resize', function(uri) {

            if (state === STATES.EXPANDED) {
                adFrame.contentWindow.broadcastEvent(EVENTS.ERROR, 'Can not expand a resized ad', 'resize');
                return;
            } else if (state === STATES.HIDDEN || state === STATES.UNKNOWN || state === STATES.LOADING) {
                return;
            }
            state = STATES.RESIZED;
            showMraidCloseButton(true);
            resizeAd();

            adBridge.pushChange({ 'state':state, 'currentPosition':currentPosition, 'size':size });
        }, this);

        bridge.addEventListener('setExpandProperties', function(properties) {
            broadcastEvent(EVENTS.INFO, 'setting expand properties to ' + stringify(properties));
            !properties.width || (expandProperties.width = properties.width);
            !properties.height || (expandProperties.height = properties.height);
            !properties.useCustomClose || (expandProperties.useCustomClose = properties.useCustomClose);

            adBridge.pushChange({'expandProperties':expandProperties});
        }, this);

        bridge.addEventListener('setResizeProperties', function(properties) {
            broadcastEvent(EVENTS.INFO, 'setting resize properties to ' + stringify(properties));
            setResizeProperties(properties);
            adBridge.pushChange({'resizeProperties':resizeProperties});
        }, this);

        bridge.addEventListener('createCalendarEvent', function(params) {
            var allow = confirm('CONFIRM: Create this calendar event?\n' + stringify(params));
            if (allow) {
                broadcastEvent(EVENTS.INFO, 'creating event ' + stringify(params));
            } else {
                adFrame.contentWindow.broadcastEvent(EVENTS.ERROR, 'Permission denied by user', 'createCalendarEvent');
            }
        }, this);

        bridge.addEventListener('setOrientationProperties', function(properties) {
            broadcastEvent(EVENTS.INFO, 'setting orientation properties to ' + stringify(properties));
            setOrientationProperties(properties);
            adBridge.pushChange({'orientationProperties':orienationProperties});
        }, this);

        bridge.addEventListener('useCustomClose', function(useCustomCloseIndicator) {
             broadcastEvent(EVENTS.INFO, 'setting useCustomClose properties to ' + stringify(useCustomCloseIndicator));
             expandProperties.useCustomClose = !!useCustomCloseIndicator;
        }, this);

        controller.addEventListener('info', function(message) {
            broadcastEvent(EVENTS.INFO, message);
        }, this);

        controller.addEventListener('error', function(message) {
            broadcastEvent(EVENTS.ERROR, message);
        }, this);

        var initProps = {
            state:STATES.LOADING,
            screenSize:screenSize,
            orientation:orientation,
            size:size,
            defaultPosition:defaultPosition,
            maxSize:maxSize,
            expandProperties:expandProperties,
            resizeProperties:resizeProperties,
            orientationProperties:orientationProperties,
            supports:supports,
            version:mraidview.version,
            placement:mraidview.placement,
            currentPosition:defaultPosition,
            isViewable:isAdViewAble()
        };

        bridge.pushChange({version:mraidview.version});
        bridge.pushChange(initProps);

        if (!!inactiveAdBridge) {
            state =  STATES.EXPANDED;

            mraidview.setOrientation(orientation, true);
            bridge.pushChange({'state':state, 'currentPosition': currentPosition });
            repaintAdWindow();
        }

        bridge.pushChange({ state:state });
    };

    var initAdFrame = function() {
        if (this.detachEvent) {
            this.detachEvent("onload", initAdFrame);
        } else {
            this.onload = '';
        }
        broadcastEvent(EVENTS.INFO, 'initializing ad frame');

        var win = this.contentWindow,
            doc = win.document,
            adScreen = {};

        for (var prop in win.screen) {
            if (prop !== 'width' && prop !== 'height') {
                adScreen[prop] = win.screen[prop];
            }
        }

        adScreen.width = screenSize.width;
        adScreen.height = screenSize.height;

        win.screen = adScreen;

        var bridgeJS = doc.createElement('script');
        bridgeJS.setAttribute('type', 'text/javascript');
        bridgeJS.setAttribute('src', 'mraidview-bridge.js');
        doc.getElementsByTagName('head')[0].appendChild(bridgeJS);

        intervalID = win.setInterval(function() {
            if (win.mraidview) {
                win.clearInterval(intervalID);

                var mraidJS = doc.createElement('script');
                mraidJS.setAttribute('type', 'text/javascript');
                mraidJS.setAttribute('src', 'mraid-main.js');
                doc.getElementsByTagName('head')[0].appendChild(mraidJS);

                intervalID = win.setInterval(function() {
                    if (win.mraid) {
                        win.clearInterval(intervalID);
                        window.clearTimeout(timeoutID);
                        initAdBridge(win.mraidview, win.mraid);
                        loadHtml(adHtml);
                    }
                }, 30);
            }
        }, 30);
    };

    /**
     * init2PartAdFrame initializes the second ad frame used by two-part ads.
     */
    var init2PartAdFrame = function(headContent) {
        if (this.detachEvent) {
            this.detachEvent("onload", init2PartAdFrame);
        } else {
            this.onload = '';
        }
        broadcastEvent(EVENTS.INFO, 'initializing ad frame for part 2');
        var win = adFrameExpanded.contentWindow,
            doc = win.document,
            adScreen = {};
        function loadPartTwo() {
            var headDump = doc.createElement('div'),
                script,
                scripts,
                scriptsCount,
                i;
            headDump.id = 'headDump';
            doc.querySelector('body').appendChild(headDump);
            headDump.innerHTML = headContent;
            scripts = headDump.getElementsByTagName("script");
            scriptsCount = scripts.length;
            for (i=0; i<scriptsCount; i++) {
                script = doc.createElement('script');
                script.type = "text/javascript";
                if (scripts[i].src !== '') {
                    script.src = scripts[i].src;
                } else {
                    var text = scripts[i].text.replace(/\\'/g, "'");
                    script.text = text;
                }
                doc.body.appendChild(script);
            }
        }

        for (var prop in win.screen) {
            if (prop !== 'width' && prop !== 'height') {
                adScreen[prop] = win.screen[prop];
            }
        }

        adScreen.width = screenSize.width;
        adScreen.height = screenSize.height;

        win.screen = adScreen;
        intervalID = win.setInterval(function() {
            if (win.mraidview) {
                win.clearInterval(intervalID);
                intervalID = win.setInterval(function() {
                    if (win.mraid) {
                        win.clearInterval(intervalID);
                        window.clearTimeout(timeoutID);
                        initAdBridge(win.mraidview, win.mraid);
                        loadPartTwo();
                    }
                }, 30);
            }
        }, 30);
    };

    var changeViewable = function (toggle) {
        if (!isViewable && isAdViewAble()) {
            isViewable = true;
            adBridge.pushChange({ 'isViewable': isViewable});
            if (inactiveAdBridge) inactiveAdBridge.pushChange({'isViewable': isViewable});
        } else if (isViewable && !isAdViewAble()) {
            isViewable = false;
            adBridge.pushChange({ 'isViewable': isViewable});
            if (inactiveAdBridge) inactiveAdBridge.pushChange({'isViewable': isViewable});
        }
    };

    var isAdViewAble = function () {
        var viewableAttr = adContainer.getAttribute('data-isViewable');
        return ((typeof(viewableAttr) === 'string' && viewableAttr === 'true') || (typeof(viewableAttr) === 'boolean' && viewableAttr));
    };

    var setAdOrientation = function (degree) {
        if (adContainerOrientation !== degree) {
            adContainerOrientation = degree;
            /* 
            * The code below has been commented for two reasons:
            * 1. The device should always report the correct orientation, so we don't want to override the
            * window.orientation property.
            * 2. There is no MRAID orientation api, so we don't need to trigger an orientationchange event. This code is here 
            * as legacy from when MRAID orientation was under consideration.
            */
            /*adFrame.contentWindow.orientation = degree;
            var orientationChangeEvent = adFrame.contentWindow.document.createEvent('HTMLEvents');
            orientationChangeEvent.initEvent('orientationchange', false, false);
            adFrame.contentWindow.dispatchEvent(orientationChangeEvent);*/
            adBridge.pushChange({'size': size, 'orientation': adContainerOrientation, 'currentPosition': currentPosition});
        }
    };

    var updateAdSize = function(val){
        setMaxAdArea(val);
        setExpandProperties(val);
    };
})();
